package rlyy.framework.core.resolver.valid;

import rlyy.framework.core.common.exception.ParameterValidException;
import rlyy.framework.core.common.request.RequestWrapper;
import rlyy.framework.core.resolver.valid.check.ValidatorFactoryUtils;
import org.springframework.core.BridgeMethodResolver;
import org.springframework.stereotype.Component;
import org.springframework.util.ClassUtils;
import org.springframework.util.StringUtils;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.method.HandlerMethod;

import javax.validation.ConstraintViolation;
import javax.validation.executable.ExecutableValidator;
import java.lang.reflect.Method;
import java.util.Optional;
import java.util.Set;

/**
 * @author : shibixiao
 * @description :
 * @date : 2021-03-23 17:18
 **/
@Component
public class MethodValidatorProcessor implements IMyValidatedPostProcessor {


    @Override
    public boolean supportsValid(RequestWrapper requestWrapper) {
        return this.determineValidation(requestWrapper) != null;
    }

    @Override
    public void validate(RequestWrapper requestWrapper) throws ParameterValidException {
        HandlerMethod handlerMethod = requestWrapper.getHandlerMethod();
        boolean enableFastFail = this.enableFastFail(handlerMethod);
        ExecutableValidator execVal = ValidatorFactoryUtils.getExecutableValidator(enableFastFail);
        Validated validatedAnn = determineValidation(requestWrapper);
        Class<?>[] groups = validatedAnn.value();
        Set<ConstraintViolation<Object>> result;
        Method methodToValidate = handlerMethod.getMethod();
        try {
            result = execVal.validateParameters(
                    handlerMethod.getBean(), methodToValidate, requestWrapper.getRequestParams(), groups);
        } catch (IllegalArgumentException ex) {
            methodToValidate = BridgeMethodResolver.findBridgedMethod(
                    ClassUtils.getMostSpecificMethod(methodToValidate, handlerMethod.getBeanType()));
            result = execVal.validateParameters(
                    handlerMethod.getBean(), methodToValidate, requestWrapper.getRequestParams(), groups);
        }

        String errorMsg = ValidatorFactoryUtils.validResult2String(result);
        if(!StringUtils.isEmpty(errorMsg)){
            throw new ParameterValidException(errorMsg);
        }
    }

    private Validated determineValidation(RequestWrapper requestWrapper) {
        HandlerMethod handlerMethod = requestWrapper.getHandlerMethod();
        return Optional.ofNullable(handlerMethod.getMethodAnnotation(Validated.class))
                .orElse(handlerMethod.getBeanType().getAnnotation(Validated.class));
    }
}
